type Fn = (...params: any[]) => any


/**实现call。 整体思路就是：谁调用的，this就指向谁 
 * @param this  要被改变的函数
 * @param context 改变后的this指向
 * @param args 剩余参数
 * @returns 返回执行结果
 */
const myCall = function <T extends Fn>(this: T, context: any, ...args: Parameters<T>): ReturnType<T> {
    context = context ?? window;// 如果context参数为空，则默认为window对象 - 缺陷：原版的call，当context为原始值时，会包装成对象的 
    const fnSymbol = Symbol(); // 使用 Symbol 函数创建一个唯一的标识符，防止和原有属性冲突 
    context[fnSymbol] = this;// 将原始函数存储为 context 对象的属性 
    const result = context[fnSymbol](...args);// 调用函数并将结果存储在 result 变量中 
    delete context[fnSymbol];// 删除 context 对象的属性 
    return result; // 返回函数的结果
}
/**实现apply。 和call的区别就是传参方式 
 * @param this  要被改变的函数
 * @param context 改变后的this指向
 * @param args 参数数组
 * @returns 返回执行结果
 */
const myApply = function <T extends Fn>(this: T, context: any, args: Parameters<T>): ReturnType<T> {
    context = context ?? window;// 如果context参数为空，则默认为window对象 - 缺陷：原版的call，当context为原始值时，会包装成对象的 
    const fnSymbol = Symbol(); // 使用 Symbol 函数创建一个唯一的标识符，防止和原有属性冲突 
    context[fnSymbol] = this;// 将原始函数存储为 context 对象的属性 
    const result = context[fnSymbol](...args);// 调用函数并将结果存储在 result 变量中 
    delete context[fnSymbol];// 删除 context 对象的属性 
    return result; // 返回函数的结果
}
/**实现bind */
const myBind = function <T extends Fn>(this: T, context: any): T {
    const fn = this
    return function (...args: Parameters<T>) {
        fn.myApply(context, args)
    } as T
}

declare global {
    interface Function {
        myCall: typeof myCall
        myApply: typeof myApply
        myBind: typeof myBind
    }
}
/**在函数原型上添加 修改this指向 的自定义函数 */
export const init = () => {
    Function.prototype.myCall = myCall
    Function.prototype.myApply = myApply
    Function.prototype.myBind = myBind
}